import { track, trigger, pauseTracking, resumeTracking } from './effect.js'
import { TrackOperationType, TriggerOperationType } from './operation.js'
import { reactive } from './reactive.js'
import { hasChanged, isObject } from './utils.js'

const arrayInstrumentations = {}
const RAW = Symbol('raw');

// 数组正常调用这些API没有问题，但是数组中嵌套了对象，调用这些API会找不到（因为对象的比较是proxy对象和原始对象的比较）
['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
  arrayInstrumentations[key] = function (...args) {
    // 1.正常找
    const result = Array.prototype[key].apply(this, args)
    // 2.找不到，重新在原始对象中找一遍
    if (result < 0 || result === false) {
      return Array.prototype[key].apply(this[RAW], args)
    }
    return result
  }
});

// 这个数组的API会触发key值为API的这个没有问题，但是也会触发length，这个是不需要的，所以需要暂停和恢复依赖收集
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(key => {
  arrayInstrumentations[key] = function (...args) {
    pauseTracking()  // 暂停收集依赖
    const result = Array.prototype[key].apply(this, args)
    resumeTracking() // 恢复收集依赖
    return result
  }
})

function get(target, key, receiver) {
  // 方便之后使用这个key值获取到target
  if (key === RAW) {
    return target
  }
  track(target, TrackOperationType.GET, key)
  // 使用receiver的原因：防止原始数据拿到的不是proxy对象（在对象嵌套对象的情况下）
  const result = Reflect.get(target, key, receiver)
  // 
  if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
    return arrayInstrumentations[key]
  }
  // 对象中嵌套对象需要深度遍历成proxy
  if (isObject(result)) {
    return reactive(result)
  }
  return result
}

function set(target, key, value, receiver) {
  const type = target.hasOwnProperty(key) ? TriggerOperationType.SET : TriggerOperationType.ADD
  // 这里不能使用Reflect.get（因为如果对象中有个get方法返回两个响应式变量的和，它会重新再收集依赖，所以不能使用）
  const oldValue = target[key]
  const oldLen = Array.isArray(target) ? target.length : undefined
  const result = Reflect.set(target, key, value, receiver)
  if (!result) {
    return result
  }
  const newLen = Array.isArray(target) ? target.length : undefined
  if (hasChanged(oldValue, value) || type === TriggerOperationType.ADD) {
    trigger(target, type, key)
    if (Array.isArray(target) && oldLen !== newLen) {
      // 直接通过赋值的方式arr[5] = 4，主动触发长度
      if (key !== 'length') {
        trigger(target, TriggerOperationType.SET, 'length')
      } else {
        // 直接变更了数组的长度
        for (let i = newLen; i < oldLen; i++) {
          trigger(target, TriggerOperationType.DELETE, i.toString())
        }
      }
    }
  }
  return result
}

// 判断对象中的属性是否存在的拦截（'a' in obj）
function has(target, key) {
  track(target, TrackOperationType.HAS, key)
  return Reflect.has(target, key)
}

// 使用迭代器的拦截（forin遍历）
function ownKeys(target) {
  track(target, TrackOperationType.INERATOR)
  return Reflect.ownKeys(target)
}

// 删除某个属性的拦截（delete obj.a）
function deleteProperty(target, key) {
  const hasKey = target.hasOwnProperty(key)
  const result = Reflect.deleteProperty(target, key)
  if (hasKey && result) {
    trigger(target, TriggerOperationType.DELETE, key)
  }
  return result
}

export const handler = {
  get,
  set,
  has,
  ownKeys,
  deleteProperty
}